package org.dromara.xxmj.service;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.constant.TenantConstants;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.enums.LoginType;
import org.dromara.common.core.enums.UserType;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.*;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.exception.TenantException;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.demo.base.Const;
import org.dromara.demo.base.ErrorCode;
import org.dromara.demo.common.XxmjException;
import org.dromara.demo.utils.LoginUtils;
import org.dromara.demo.utils.ValidateUtil;
import org.dromara.system.domain.SysClient;
import org.dromara.system.domain.SysPost;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.domain.vo.*;
import org.dromara.system.mapper.SysClientMapper;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ISysUserService;
import org.dromara.xxmj.domain.XxmjUserExt;
import org.dromara.xxmj.domain.bo.XxmjUserExtBo;
import org.dromara.xxmj.domain.dto.ForgetPasswordDTO;
import org.dromara.xxmj.domain.dto.UserLoginDTO;
import org.dromara.xxmj.domain.model.XxmjRegisterBody;
import org.dromara.xxmj.domain.vo.CourseVo;
import org.dromara.xxmj.domain.vo.UserLoginVo;
import org.dromara.xxmj.domain.vo.XxmjUserExtVo;
import org.dromara.xxmj.mapper.CourseMapper;
import org.dromara.xxmj.mapper.XxmjUserExtMapper;
import org.dromara.xxmj.utils.CheckUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.Date;

/**
 * 小巷密卷注册校验方法
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class XxmjRegisterService {

    private final ISysUserService userService;

    private final SysUserMapper sysUserMapper;

    private final XxmjUserExtMapper xxmjUserExtMapper;

    private final LoginUtils loginUtils;

    private final CheckUtils checkUtils;

    private final IXxmjUserExtService userExtService;

    private final SysClientMapper sysClientMapper;

    @Resource
    private CourseMapper courseMapper;
    /**
     * 注册
     */
    @RepeatSubmit
    @Transactional(rollbackFor = Exception.class)
    public void register(XxmjRegisterBody userLoginDTO) {
        String password = userLoginDTO.getPassword(); //密码
        String userCategory = userLoginDTO.getUserCategory(); //用户分类
        String phoneNumber = userLoginDTO.getPhoneNumber(); //手机号码

        loginUtils.VerifyPasswordResult(password, userLoginDTO.getConfirmPassword());

        //验证数据库中是否有相同用户
        if (sysUserMapper.exists(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phoneNumber))) {
            throw new ServiceException("您输入的手机号码已注册，请直接登录。");
        }

        //校验验证码
        // loginUtils.validateCaptcha(userLoginDTO.getPhoneNumber(), userLoginDTO.getCode(), userLoginDTO.getPhoneNumber());

        // 创建用户
        SysUserBo sysUser = new SysUserBo();
        sysUser.setNickName(userLoginDTO.getPhoneNumber());
        sysUser.setUserName(userLoginDTO.getUsername());
        sysUser.setPassword(BCrypt.hashpw(password));
        sysUser.setPhonenumber(phoneNumber);
        sysUser.setUserType(UserType.APP_USER.getUserType());

        // 注册用户
        userService.registerUser(sysUser);

        // 创建用户扩展信息
        XxmjUserExtBo userExt = new XxmjUserExtBo();
        userExt.setUserId(sysUser.getUserId());
        userExt.setUserCategory(userCategory);
        userExt.setPhoneVerified("1"); // 默认验证
        userExt.setRegisterTime(new Date());
        userExtService.insertByBo(userExt);

        loginUtils.recordLogininfor(userLoginDTO.getPhoneNumber(), Constants.REGISTER, MessageUtils.message("user.register.success"));
    }

    /**
     * 登录
     * @param
     * @return
     */
    public UserLoginVo userLogin(String body) {
        UserLoginDTO userLoginDTO = JsonUtils.parseObject(body, UserLoginDTO.class);
        ValidatorUtils.validate(userLoginDTO);

        String phoneNumber = userLoginDTO.getPhoneNumber();
        String password = userLoginDTO.getPassword();
        String code = userLoginDTO.getCode();
        //如果验证码和密码都为空
        if (StringUtils.isBlank(password) && StringUtils.isBlank(code)){
            throw new ServiceException("登录操作异常");
        }

        boolean loginStatus = false;
        if (!StringUtils.isBlank(password)){
            SysUserVo user = loadUserByUsername(phoneNumber);
            loginUtils.checkLogin(LoginType.PASSWORD, phoneNumber, () -> !BCrypt.checkpw(password, user.getPassword()));
            loginStatus = true;
        } else if (!StringUtils.isBlank(code) && StringUtils.isNotBlank(userLoginDTO.getUuid())) {
            loginUtils.validateCaptcha(userLoginDTO.getPhoneNumber(), code, userLoginDTO.getUuid());
            loginStatus = true;
        }

        if (!loginStatus){
            throw new ServiceException("登录操作异常");
        }

        LoginUser loginUser = TenantHelper.dynamic(phoneNumber, () -> {
            SysUserVo user = loadUserByUsername(phoneNumber);
            return buildLoginUser(user);
        });

        SaLoginModel model = new SaLoginModel();
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
        // 例如: 后台用户30分钟过期 app用户1天过期
        model.setTimeout(Const.APP_TIME_OUT);
        model.setActiveTimeout(Const.APP_ACTIVITE_TIME_OUT);
        model.setExtra(LoginHelper.CLIENT_KEY, "000000");
        // 生成token
        LoginHelper.login(loginUser, model);

        UserLoginVo loginVo = new UserLoginVo();
        loginVo.setAccessToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        return loginVo;
    }

    /**
     * 忘记密码
     *
     * @Author 王晨沣 2307105054
     * @Date 2025-05-17 16:04:44
     */
    public void forgetPassword(ForgetPasswordDTO dto) {
        ValidatorUtils.validate(dto);

        SysUserVo user = sysUserMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, dto.getPhoneNumber()));
        if (user == null){
            throw new ServiceException("您的账号不存在,请重新输入或注册!");
        }
        String uuid = Convert.toStr(user.getUserId());

        checkUtils.checkUser(user);
        // loginUtils.validateCaptcha(dto.getPhoneNumber(), dto.getCode(), uuid);

        SysUser post = MapstructUtils.convert(dto, SysUser.class);

        //忽略数据权限，不需要登录
        DataPermissionHelper.ignore(()->{
            sysUserMapper.update(post,new LambdaUpdateWrapper<SysUser>()
                .set(SysUser::getPassword, BCrypt.hashpw(dto.getPassword()))
                .set(SysUser::getUpdateTime,new Date())
                .eq(SysUser::getUserId, uuid));
        });

    }


    private LoginUser buildLoginUser(SysUserVo user) {
        LoginUser loginUser = new LoginUser();
        Long userId = user.getUserId();
        loginUser.setUserId(userId);
        loginUser.setUsername(user.getUserName());
        loginUser.setNickname(user.getNickName());
        loginUser.setUserType(user.getUserType());
        return loginUser;
    }


    private SysUserVo loadUserByUsername(String phoneNumber) {
        SysUserVo user = sysUserMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phoneNumber));
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户：{} 不存在.", phoneNumber);
            throw new UserException("user.not.exists", phoneNumber);
        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
            log.info("登录用户：{} 已被停用.", phoneNumber);
            throw new UserException("user.blocked", phoneNumber);
        }
        return user;
    }


    public void userLogout(String code) {
        Long userId = LoginHelper.getUserId();
        if (ObjectUtil.isNull(userId)) {
            throw new ServiceException("用户未登录");
        }

        SysUserVo user = sysUserMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserId, userId));
        checkUtils.checkUser(user);

        loginUtils.validateCaptcha(user.getPhonenumber(), code, Convert.toStr(user.getUserId()));

        //将手机号置空，并将用户名改为手机号
        LambdaUpdateWrapper<SysUser> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(SysUser::getUserId, userId).set(SysUser::getDelFlag, SystemConstants.DELETED)
            .set(SysUser::getUpdateTime, new Date()).set(SysUser::getPhonenumber, null).set(SysUser::getUserName, user.getPhonenumber());

        RedisUtils.addCacheList(SystemConstants.LOGOUT_USER_INFO, user);
        sysUserMapper.update(wrapper);
        StpUtil.logout();
    }

    public CourseVo getCourse(String courseId) {
        ValidateUtil.validateStringField(courseId, "courseId");
        return courseMapper.selectVoById(courseId);
    }

}
